package com.example.mq.mqclient;

import com.example.mq.common.*;
import com.example.mq.mqserver.core.BasicProperties;
import com.example.mq.mqserver.core.ExchangeType;
import lombok.Data;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  表示一个逻辑上的连接  需要提供一系列的方法  和服务器提供的核心API对应
 */
@Data
public class Channel {

    private String channelId;
    //当前channel 属于那个TCP连接
    private Connection connection;
    //存储客户端收到的服务器的响应
    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap=new ConcurrentHashMap<>();
    //如果当前的 Channel 订阅了某个队列 就需要记录下回调是啥 当该队列的消息返回时 调用回调
    private Consumer consumer=null;

    public Channel(String channelId, Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
    }


    //在这个方法中 和服务器进行交互 告诉服务器 客户端创建了新的连接
    public boolean createChannel() throws IOException {
        System.out.println("创建channel连接");
        //构造请求
        Request request=new Request();
        request.setType(0x1);
        //对于创建 Channel 来说 payload 就是一个 basicArguments 对象
        BasicArguments arguments=new BasicArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload= BinaryTool.toBytes(arguments);
        request.setLength(payload.length);
        request.setPayload(payload);
        //构造出完整请求后 发送请求
        connection.writeRequest(request);
        //等待响应结果
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //期望使用这个方法 阻塞等待服务器的响应.
    private BasicReturns waitResult(String rid) {
        BasicReturns basicReturns=null;
        while ((basicReturns=basicReturnsMap.get(rid))==null){
            //如果查询结果为空 说明该响应还没响应完成
            //此时就需要阻塞等待响应回来
            synchronized (this){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //读取成功后 还需要把这个消息从哈希表中删除
        basicReturnsMap.remove(rid);
        return basicReturns;
    }

    public void putReturns(BasicReturns basicReturns) {
        basicReturnsMap.put(basicReturns.getRid(),basicReturns);
        //唤醒等待的线程
        synchronized (this){
            notifyAll();
        }
    }

    private String generateRid(){
        return "R-"+UUID.randomUUID().toString();
    }

    //关闭channel 给服务器发送一个 type 发送一个0x2的请求
    public boolean close() throws IOException {
        System.out.println("关闭channel连接");
        BasicArguments arguments=new BasicArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x2);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());

        return basicReturns.isOk();
    }

    //创建交换机
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable, boolean autoDelete,
                                    Map<String,Object> arguments) throws IOException {
        System.out.println("创建交换机 exchangeName: "+exchangeName);
        //构造数据
        ExchangeDeclareArguments exchangeDeclareArguments=new ExchangeDeclareArguments();
        exchangeDeclareArguments.setChannelId(channelId);
        exchangeDeclareArguments.setExchangeName(exchangeName);
        exchangeDeclareArguments.setRid(generateRid());
        exchangeDeclareArguments.setExchangeType(exchangeType);
        exchangeDeclareArguments.setAutoDelete(autoDelete);
        exchangeDeclareArguments.setDurable(durable);
        exchangeDeclareArguments.setArguments(arguments);
        //转换保存payload数据
        byte[] payload=BinaryTool.toBytes(exchangeDeclareArguments);
        //构造请求
        Request request=new Request();
        request.setType(0x3);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        BasicReturns basicReturns = waitResult(exchangeDeclareArguments.getRid());
        return basicReturns.isOk();
    }

    //删除交换机
    public boolean exchangeDelete(String exchangeName) throws IOException {
        System.out.println("删除交换机 exchangeName: "+exchangeName);
        //构造数据
        ExchangeDeleteArguments arguments=new ExchangeDeleteArguments();
        arguments.setExchangeName(exchangeName);
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //创建队列
    public boolean queueDeclare(String queueName,boolean durable,boolean exclusive,boolean autoDelete,
                                 Map<String,Object> arguments) throws IOException {
        System.out.println("创建队列 queueName: "+queueName);
        //构造数据
        QueueDeclareArguments queueDeclareArguments=new QueueDeclareArguments();
        queueDeclareArguments.setRid(generateRid());
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setArguments(arguments);
        queueDeclareArguments.setQueueName(queueName);
        //转换数据
        byte[] payload=BinaryTool.toBytes(queueDeclareArguments);
        //构造请求
        Request request=new Request();
        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(queueDeclareArguments.getRid());
        return basicReturns.isOk();
    }

    //删除队列
    public boolean queueDelete(String queueName) throws IOException {
        System.out.println("删除队列 queueName: "+queueName);
        //构造数据
        QueueDeleteArguments arguments=new QueueDeleteArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setQueueName(queueName);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x6);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //创建绑定
    public boolean queueBind(String exchangeName,String queueName,String bindingKey) throws IOException {
        System.out.println("创建绑定 exchangeName: "+exchangeName+" queueName: "+queueName+" bindingKey: "+bindingKey);
        //构造数据
        QueueBindArguments arguments=new QueueBindArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        arguments.setQueueName(queueName);
        arguments.setExchangeName(exchangeName);
        arguments.setBindingKey(bindingKey);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }
    //删除绑定
    public boolean queueUnbind(String exchangeName,String queueName) throws IOException {
        System.out.println("删除绑定 exchangeName: "+exchangeName+" queueName: "+queueName);
        //构造数据
        QueueUnbindArguments arguments=new QueueUnbindArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setQueueName(queueName);
        arguments.setExchangeName(exchangeName);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //发送消息
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties,byte[] body) throws IOException {
        System.out.println("发送消息 exchangeName: "+exchangeName+" routingKey: "+routingKey+" basicProperties: "+basicProperties+" body: "+body);
        //构造数据
        BasicPublishArguments arguments=new BasicPublishArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setExchangeName(exchangeName);
        arguments.setRoutingKey(routingKey);
        arguments.setBasicProperties(basicProperties);
        arguments.setBody(body);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //订阅消息
    public boolean BasicConsume(String queueName,boolean autoAck,Consumer consumer) throws MqException, IOException {
        System.out.println("订阅消息 queueName: "+queueName+" autoAck: "+autoAck);
        //设置回调方法
        if(this.consumer!=null){
            throw new MqException("该 channel 已经设置过消费消息的回调了 不能重复设置");
        }
        this.consumer=consumer;
        //构造数据
        BasicConsumeArguments arguments=new BasicConsumeArguments();
        arguments.setConsumerTag(channelId);  //此处的 ConsumerTag 也使用 channelId 来表示
        arguments.setRid(generateRid());
        arguments.setQueueName(queueName);
        arguments.setAutoAck(autoAck);
        arguments.setChannelId(channelId);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //确认消息
    public boolean BasicAck(String queueName,String messageId) throws IOException {
        System.out.println("确认消息 queueName: "+queueName+" messageId: "+messageId);
        //构造方法
        BasicAckArguments arguments=new BasicAckArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setQueueName(queueName);
        arguments.setMessageId(messageId);
        //转换数据
        byte[] payload=BinaryTool.toBytes(arguments);
        //构造请求
        Request request=new Request();
        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);
        //发送请求
        connection.writeRequest(request);
        //阻塞等待响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

}
